package zy.blue7.flowable.service;

import org.apache.commons.io.FileUtils;
import org.flowable.common.engine.impl.util.IoUtil;
import org.flowable.engine.*;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:flowable-context.xml")
public class RepositoryServiceTest {

    /**
     * todo RepositoryService类说明
     *
     * todo 是flowable的仓库服务类，所谓的仓库服务类指流程定义流程文档的两个文件： bpmn文件  和  流程图片
     *
     * todo 流程图片生成的支持方式
     *  1.设计流程的时候产生
     *  2.通过api方式根据流程文件生成
     */

    /**
     *
     * todo RepositoryService类获取方式
     * 引擎配置类构造代码  ProcessEngineConfigurationImpl
     * todo
     *     protected RepositoryService repositoryService = new RepositoryServiceImpl();
     *
     *
     *     protected RuntimeService runtimeService = new RuntimeServiceImpl();
     *     protected HistoryService historyService = new HistoryServiceImpl(this);
     *     protected IdentityService identityService = new IdentityServiceImpl(this);
     *     protected TaskService taskService = new TaskServiceImpl(this);
     *     protected FormService formService = new FormServiceImpl();
     *     protected ManagementService managementService = new ManagementServiceImpl();
     *     protected DynamicBpmnService dynamicBpmnService = new DynamicBpmnServiceImpl(this);
     *
     */


    /**
     * todo
     * RepositoryService类可以派生的类
     * 1.DeploymentBuilder      用来定义流程部署的相关参数
     * 2.ProcessDefinitionQuery        用来构造查询流程定义相关参数
     * 3.NativeProcessDefinitionQuery      用来构造本地sql查询流程定义相关参数
     * 4.DeploymentQuery                   用来构造查询部署对象相关参数
     */


    ProcessEngine processEngine;

    RepositoryService repositoryService;

    @Before
    public void testProcessEngine() {

        /**
         * 获取默认的流程引擎类
         */
        //todo 主要是通过flowable的风格的配置文件  创建一个 {@see org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration} 配置类
        //todo 该配置类 中创建了 多个service， 然后赋值给processengine
        processEngine = ProcessEngines.getDefaultProcessEngine();

        System.out.println("默认的流程引擎类 " + processEngine);

        //todo 获取的service

        DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
        System.out.println(dynamicBpmnService);

        FormService formService = processEngine.getFormService();
        System.out.println(formService);

        HistoryService historyService = processEngine.getHistoryService();
        System.out.println(historyService);

        IdentityService identityService = processEngine.getIdentityService();
        System.out.println(identityService);

        ManagementService managementService = processEngine.getManagementService();
        System.out.println(managementService);

        repositoryService = processEngine.getRepositoryService();
        System.out.println(repositoryService);

        RuntimeService runtimeService = processEngine.getRuntimeService();
        System.out.println(runtimeService);

        TaskService taskService = processEngine.getTaskService();
        System.out.println(taskService);

        ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();
        System.out.println(processEngineConfiguration);

        String name = processEngine.getName();
        System.out.println("流程引擎的名字：" + name);

    }


    /**
     * 1.DeploymentBuilder      用来定义流程部署的相关参数
     */
    @Test
    public void deploymentBuilder() {

        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                .category("测试分类")
                .name("名称");
        System.out.println(deploymentBuilder + "========================-----------------------------------------");

    }


    /**
     * 资源部署方式---》todo 其实也就是如何将流程文档部署到数据库
     *
     *  todo 文档部署方式
     *  1.classpath方式
     *  2.文本方式
     *  3.todo 模型方式
     *  4.字节方式
     *  5.流方式
     *      5.1 输入流 只能部署一个文档
     *      5.2 压缩流  可以部署多个文档
     *
     */


    /**
     * classpath 方式部署 资源文档
     */
    @Test
    public void deployByClassPath() {

        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                //todo 下面的方法都是给deployment这个表添加属性等等。。。
                /**
                 * todo 注意这里的category 只是给部署信息表更改类别，但是流程定义信息的类别 是根据流程定义文件内容来决定的，默认是bpmn中的 targetNamespace
                 * <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
                 */
                .category("测试分类")
                .name("名称")
                //todo 设置 calsspath路径下的bpmn文件
                .addClasspathResource("test.bpmn20.xml");

        //todo 部署
        Deployment deployment = deploymentBuilder.deploy();
        System.out.println("deployment:==== " + deployment.getId());  //id
        System.out.println("deployment:==== " + deployment.getKey());  //key
        System.out.println("deployment:==== " + deployment.getCategory());  //类别
        System.out.println("deployment:==== " + deployment.getParentDeploymentId());  //父类部署id
        System.out.println("deployment:==== " + deployment.getDerivedFrom());
        System.out.println("deployment:==== " + deployment.getDerivedFromRoot());
        System.out.println("deployment:==== " + deployment.getEngineVersion());    //引擎版本
        System.out.println("deployment:==== " + deployment.getTenantId());         //租户id
        /**
         * todo 在部署的时候会向数据库中插入两条记录，分别存储 bpmn。xml文件和 对于的图片，存储在 act_ge_bytearray这个表中，
         * todo 所以可以下载bpmn。xml文件的图片
         */
        System.out.println("deployment:==== " + deployment.getResources());         //资源
        System.out.println("deployment:==== " + deployment.getClass());


        /**
         * 执行了一下操作
         * insert into ACT_RE_PROCDEF(ID_, REV_, CATEGORY_, NAME_, KEY_, VERSION_, DEPLOYMENT_ID_, RESOURCE_NAME_, DGRM_RESOURCE_NAME_, DESCRIPTION_, HAS_START_FORM_KEY_, HAS_GRAPHICAL_NOTATION_ , SUSPENSION_STATE_, DERIVED_FROM_, DERIVED_FROM_ROOT_, DERIVED_VERSION_, TENANT_ID_, ENGINE_VERSION_) values (?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
         * insert into ACT_RE_DEPLOYMENT(ID_, NAME_, CATEGORY_, KEY_, TENANT_ID_, DEPLOY_TIME_, DERIVED_FROM_, DERIVED_FROM_ROOT_, PARENT_DEPLOYMENT_ID_, ENGINE_VERSION_) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
         * INSERT INTO ACT_GE_BYTEARRAY(ID_, REV_, NAME_, BYTES_, DEPLOYMENT_ID_, GENERATED_) VALUES (?, 1, ?, ?, ?, ?) , (?, 1, ?, ?, ?, ?)
         *
         *
         *
         */

        /**
         * todo classpath 方式说明
         *
         * 1.首先获取流程引擎对象： 在创建时自动加载classpath路径下的 flowable.cfg.xml
         * 2.首先获取默认的流程引擎对象 通过流程引擎获取了 一个 RepositoryService对象 （仓库对象）
         * 3.由仓库对象的服务对象产生以恶搞部署对象配置对象 deploymentBuilder， 用来封装部署操作的相关配置。
         * 4.这是一个链式编程，在部署配置对象中设置显示名 等等，上传流程定义规则文件
         * 5.向数据库表中存放流程定义的规则信息
         *
         */

    }

    /**
     * 其他方式 部署资源，
     * todo 无论通过什么方式部署资源，数据库 中都会存储对应的数据
     */
    @Test
    public void deployByOther() {

        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                //todo 下面的方法都是给deployment这个表添加属性等等。。。
                .category("测试分类")
                .name("名称")
                //todo 设置 calsspath路径下的bpmn文件
//                .addClasspathResource("test.bpmn20.xml");
//                .addBytes(null,null);// 字节数组
//                .addInputStream("",null);//输入流
//                .addZipInputStream(null);//压缩流
//                .addString("","");//文本
                .addBpmnModel("", null);//模型

        Deployment deploy = deploymentBuilder.deploy();
    }


    //-----------------------------------------------------------------------------------

    /**
     * 流程部署涉及到的三张表
     * <p>
     * act_re_deployment 部署对象表  存放流程定义的显示名和部署时间，每部署一次增加一条记录
     * act_re_procdef  流程定义表    存放流程定义的属性信息，部署每个新的流程定义都会在这张表中增加一个记录。
     * todo 当流程定义的key相同的情况下，使用的是版本升级。   注意这个表中 有一个 字段跟 act_re_deployment表的主键相关联
     * <p>
     * act_ge_bytearry  资源文件表
     * todo 存储流程定义相关的部署信息，即流程定义文档的存放地 。
     * 每部署一次 就会增加两两条记录，一条是关于bpmn规则文件的，一条是图片的（如果部署时只指定了bpmn文件， flowable 会在部署的时候解析bpmn文件内容 自动生成一张 流程图）
     * 两个文件都不是很大，都是以二进制的形式存放在数据库中
     */


    /**
     * todo 文本方式部署，其实就是通过字符串部署，把配置文件转换成字符串
     * <p>
     * <p>
     * todo 注意 资源文件名称 必须规范 {@see org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil}
     * public static final String[] BPMN_RESOURCE_SUFFIXES = new String[]{"bpmn20.xml", "bpmn"};
     * public static final String[] DIAGRAM_SUFFIXES = new String[]{"png", "jpg", "gif", "svg"};
     */
    @Test
    public void deployByText() {
        //todo 将配置文件转换成字符串文本
        String text = IoUtil.readFileAsString("test.bpmn20.xml");
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                //todo 下面的方法都是给deployment这个表添加属性等等。。。
                /**
                 * todo 注意这里的category 只是给部署信息表更改类别，但是流程定义信息的类别 是根据流程定义文件内容来决定的，默认是bpmn中的 targetNamespace
                 * <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
                 */
                .category("测试分类")
                .name("名称")
                .key("测试的key")
                //todo 设置 calsspath路径下的bpmn文件
                .addString("zy.bpmn20.xml", text);
        //todo 部署
        Deployment deployment = deploymentBuilder.deploy();
        System.out.println("deployment:==== " + deployment.getId());  //id
        System.out.println("deployment:==== " + deployment.getKey());  //key
        System.out.println("deployment:==== " + deployment.getCategory());  //类别
        System.out.println("deployment:==== " + deployment.getParentDeploymentId());  //父类部署id
        System.out.println("deployment:==== " + deployment.getDerivedFrom());
        System.out.println("deployment:==== " + deployment.getDerivedFromRoot());
        System.out.println("deployment:==== " + deployment.getEngineVersion());    //引擎版本
        System.out.println("deployment:==== " + deployment.getTenantId());         //租户id
        /**
         * todo 在部署的时候会向数据库中插入两条记录，分别存储 bpmn。xml文件和 对于的图片，存储在 act_ge_bytearray这个表中，
         * todo 所以可以下载bpmn。xml文件的图片
         */
        System.out.println("deployment:==== " + deployment.getResources());         //资源
        System.out.println("deployment:==== " + deployment.getClass());
    }

    /**
     * todo 输入流方式 部署流程文件
     * <p>
     * <p>
     * todo 注意 资源文件名称 必须规范 {@see org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil}
     * public static final String[] BPMN_RESOURCE_SUFFIXES = new String[]{"bpmn20.xml", "bpmn"};
     * public static final String[] DIAGRAM_SUFFIXES = new String[]{"png", "jpg", "gif", "svg"};
     */
    @Test
    public void deployByInputStream() {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("test.bpmn20.xml");
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                //todo 下面的方法都是给deployment这个表添加属性等等。。。
                /**
                 * todo 注意这里的category 只是给部署信息表更改类别，但是流程定义信息的类别 是根据流程定义文件内容来决定的，默认是bpmn中的 targetNamespace
                 * <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
                 */
                .category("测试分类")
                .name("名称")
                .key("测试的key")
                .addInputStream("blue7.bpmn20", inputStream);
        //todo 部署
        Deployment deployment = deploymentBuilder.deploy();
        System.out.println("deployment:==== " + deployment.getId());  //id
        System.out.println("deployment:==== " + deployment.getKey());  //key
        System.out.println("deployment:==== " + deployment.getCategory());  //类别
        System.out.println("deployment:==== " + deployment.getParentDeploymentId());  //父类部署id
        System.out.println("deployment:==== " + deployment.getDerivedFrom());
        System.out.println("deployment:==== " + deployment.getDerivedFromRoot());
        System.out.println("deployment:==== " + deployment.getEngineVersion());    //引擎版本
        System.out.println("deployment:==== " + deployment.getTenantId());         //租户id
        /**
         * todo 在部署的时候会向数据库中插入两条记录，分别存储 bpmn。xml文件和 对于的图片，存储在 act_ge_bytearray这个表中，
         * todo 所以可以下载bpmn。xml文件的图片
         */
        System.out.println("deployment:==== " + deployment.getResources());         //资源
        System.out.println("deployment:==== " + deployment.getClass());
    }


//    -----------------------------------------------------------------------------------------------------------

    /**
     * todo 查询流程定义信息 ，就是存储流程定义信息的记录
     * todo 对应的表是  act_re_procdef
     */
    @Test
    public void createProcessDefinitionQuery() {
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
                .latestVersion()//查询的时候最后一个版本
                .processDefinitionName("")//流程定义的名字，name
                //todo 等等多种查询条件
                .list();
        for (ProcessDefinition processDefinition : list) {
            System.out.println("============================");
            System.out.println(processDefinition.getId());
            /**
             * todo 注意这里的category 只是给部署信息表更改类别，但是流程定义信息的类别 是根据流程定义文件内容来决定的，默认是bpmn中的 targetNamespace
             * <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
             */
            System.out.println(processDefinition.getCategory()); //流程定义类别，默认是对于bpmn文件中
            System.out.println(processDefinition.getKey());  //流程定义key
            System.out.println(processDefinition.getName()); //流程定义名字
            System.out.println(processDefinition.getResourceName());  //流程资源名称
            System.out.println(processDefinition.getDiagramResourceName());//流程资源流程图名字
            System.out.println(processDefinition.getEngineVersion());
            System.out.println(processDefinition.getDeploymentId());   //对应部署表的id
            System.out.println(processDefinition.getDerivedFrom());
            System.out.println(processDefinition.getDerivedFromRoot());
            System.out.println(processDefinition.getDerivedVersion());
            System.out.println(processDefinition.getTenantId());
        }
    }


    /**
     * todo 删除流程定义说明
     */
    @Test
    public void deleteDeploymentOrCascade() {
        /**
         * todo 1.因为删除的是流程定义，而流程定义是的部署是属于仓库服务的， 所以应该先得到 repositoryService
         * todo 2.如果该流程定义下没有正在运行的流程，则可以使用普通删除，如果有相关联的信息，使用级联删除。项目开发过程中使用级联删除的情况比较多，删除操作一般只开放给超级管理员使用
         *
         */
        //todo 级联删除  会删除当前流程定义下的所有的流程实例
        repositoryService.deleteDeployment("deployment", true);
        //todo 普通删除
        repositoryService.deleteDeployment("deployment");
    }

    /**
     * 查看图片
     * <p>
     * todo 获取流程定义文档的资源 说明
     * deploymentId 是流程部署ID
     * resourceName 为act_ge_bytearry 表中的NAME_ 列的值
     * 使用 repositoryService 的 getDeploymentResourceNames 方法可以获取指定部署下的所有文件的名称
     * 使用 repositoryService 的 getResourceAsStream 方法 传入 流程部署id和 资源的名称可以获取 该部署下指定名称文件的 输入流
     * 最后的io操作，使用commons包下的工具类
     */
    @Test
    public void viewImage() throws IOException {
        String deploymentId = "5001";
        List<String> deploymentResourceNames = repositoryService.getDeploymentResourceNames(deploymentId);
        for (String deploymentResourceName : deploymentResourceNames) {
            System.out.println("资源名称：" + deploymentResourceName);
        }

        String imageName = null;
        for (String name : deploymentResourceNames) {
            if (name.indexOf(".png") > 0)
                imageName = name;
        }
        if (imageName != null) {

            File imageFile = new File("a.png");
            /**
             * 传入部署id和 资源名称 todo 因为一个部署有多个 资源
             */
            InputStream inputStream = repositoryService.getResourceAsStream(deploymentId, imageName);
            FileUtils.copyInputStreamToFile(inputStream, imageFile);
        }

    }


//    ----------------------------------------------------------------------------------

    /**
     * 本地sql查询
     * <p>
     * 当flowable的 数据库操作不满足时，可以自定义sql 语句进行查询
     */
    @Test
    public void createNativeDeploymentQuery() {

        List<Deployment> list = repositoryService.createNativeDeploymentQuery()
                .sql("select * from aa.... where xx=11")//todo 一定要写 这个方法 sql，写入sql语句
                .list();

        //todo model 本地sql 查询等等
        Model model = repositoryService.createNativeModelQuery()
                .sql("")
                .singleResult();

        //todo processDefinition本地sql查询
        ProcessDefinition processDefinition = repositoryService.createNativeProcessDefinitionQuery()
                .sql("")
                .singleResult();
    }
}
